Skip to content

feat(bot): add playtop, playskip, skipto, seek, replay and pause/resume toggle - #522

Merged
LucasSantana-Dev merged 3 commits into
mainfrom
feat/music-quick-wins
Apr 10, 2026
Merged

LucasSantana-Dev merged 3 commits into
mainfrom
feat/music-quick-wins

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

Implements 7 music quick wins from Rythm bot parity analysis:

  • pause toggle: /pause now toggles between pause and resume (removed /resume)
  • /replay: restarts current track from beginning (seek(0))
  • /seek <time>: jump to position in current track (mm:ss or ss format)
  • /skipto <position>: skip to specific queue position
  • /playtop <query>: add song to front of queue (plays next)
  • /playskip <query>: add to front + immediately skip current track
  • /play provider param: optional provider choice (spotify/youtube/soundcloud)

Test plan

  • All 7 features covered by Jest tests
  • 1699 tests passing, 150 test suites
  • npm run verify passes

Summary by CodeRabbit

  • New Features

    • Added provider selection to /play (YouTube, Spotify, SoundCloud)
    • Added /playskip to play a track immediately
    • Added /playtop to queue a track to play next
    • Added /seek to jump to a timestamp
    • Added /skipto to jump to a queue position
  • Improvements

    • /pause now toggles pause/resume
    • Replaced /resume with /replay to restart the current track from the beginning

@vercel

vercel Bot commented Apr 10, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 10, 2026 4:58pm

Request Review

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file bot labels Apr 10, 2026
@coderabbitai

coderabbitai Bot commented Apr 10, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Converted multiple music commands: /pause now toggles pause/resume; resume renamed to replay (seek to 0); play gains an optional provider choice and delegates query resolution; added playskip, playtop, seek, and skipto; introduced shared play/query utilities and updated tests accordingly.

Changes

Cohort / File(s) Summary
Pause / Replay
packages/bot/src/functions/music/commands/pause.ts, packages/bot/src/functions/music/commands/pause.spec.ts, packages/bot/src/functions/music/commands/replay.ts, packages/bot/src/functions/music/commands/replay.spec.ts, packages/bot/src/functions/music/commands/resume.spec.ts
/pause changed into a toggle (calls queue.node.resume() if paused, otherwise queue.node.pause()); replaced resume command with replay that calls queue.node.seek(0). Tests updated/added; old resume spec removed.
Play & Query Utils
packages/bot/src/functions/music/commands/play/index.ts, packages/bot/src/functions/music/commands/play/queryUtils.ts
/play now accepts an optional provider option and delegates search engine resolution to new queryUtils.resolveSearchEngine. Added executePlayAtTop with defer/unknown-interaction handling, searchEngine resolution, player invocation, queue manipulation, and error handling.
New Play-Top/Playskip Commands
packages/bot/src/functions/music/commands/playtop.ts, packages/bot/src/functions/music/commands/playtop.spec.ts, packages/bot/src/functions/music/commands/playskip.ts, packages/bot/src/functions/music/commands/playskip.spec.ts
Added playtop and playskip commands which call shared executePlayAtTop with appropriate flags; tests added validating player calls, queue insertion/removal, skipping behavior, and reply/embed behavior.
Seek / SkipTo Commands
packages/bot/src/functions/music/commands/seek.ts, packages/bot/src/functions/music/commands/seek.spec.ts, packages/bot/src/functions/music/commands/skipto.ts, packages/bot/src/functions/music/commands/skipto.spec.ts
Added /seek (parses ss or mm:ss, validates range/duration, calls queue.node.seek) and /skipto (integer position -> queue.node.skipTo) with validation, embed/error responses, and tests.
Test Wiring
packages/bot/src/functions/music/commands/queueResolverWiring.spec.ts
Removed resume from the queue resolver wiring matrix; other wiring tests unchanged.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant InteractionHandler as Interaction
participant ClientPlayer as Player
participant QueueResolver as Queue
participant EmbedBuilder as Embed
User->>Interaction: /play (query + optional provider)
Interaction->>Player: client.player.play(channel, query, { searchEngine })
Player-->>Interaction: playedResult (track or playlist)
Interaction->>QueueResolver: resolveGuildQueue(guildId)
QueueResolver-->>Interaction: queue
Interaction->>Queue: removeExistingInstance(track) / insertAtTop(track)
alt skipCurrent
Interaction->>Queue: node.skip()
end
Interaction->>EmbedBuilder: buildPlayResponseEmbed(kind, track, position)
Interaction->>User: reply(with embeds and music control buttons)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main features introduced: playtop, playskip, skipto, seek, replay commands and pause/resume toggle functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/music-quick-wins

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 10, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 324 kB

ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/ActionPanel-DNyixsRN.js 400 B
packages/frontend/dist/assets/api-DsLAIu-l.js 2.9 kB
packages/frontend/dist/assets/authStore-Bo_PbbZd.js 557 B
packages/frontend/dist/assets/AutoMessages-CMx6p29r.js 2.69 kB
packages/frontend/dist/assets/AutoMod-H-4xwkmt.js 4.09 kB
packages/frontend/dist/assets/avatar-BYVlC9R1.js 1.79 kB
packages/frontend/dist/assets/badge-kekZ5uai.js 498 B
packages/frontend/dist/assets/Button-Chq_jJX6.js 766 B
packages/frontend/dist/assets/Card-DY7pU8Q5.js 457 B
packages/frontend/dist/assets/chunk-QFMPRPBF-ySVqcpZB.js 14.8 kB
packages/frontend/dist/assets/CommandsConfig-D5HhVE8J.js 1.49 kB
packages/frontend/dist/assets/Config-D0yyrS5J.js 1.74 kB
packages/frontend/dist/assets/CustomCommands-D6roNyDQ.js 2.15 kB
packages/frontend/dist/assets/DashboardOverview-B7ZvaNvW.js 3.42 kB
packages/frontend/dist/assets/dialog-DCGuWOQz.js 949 B
packages/frontend/dist/assets/dist-Bzdj1j8i.js 416 B
packages/frontend/dist/assets/EmbedBuilder-Rq0Aksdt.js 3.35 kB
packages/frontend/dist/assets/EmptyState-D-FW84dc.js 432 B
packages/frontend/dist/assets/Features-CWZ4j0-1.js 2.85 kB
packages/frontend/dist/assets/GuildAutomation-K29WisHS.js 2.94 kB
packages/frontend/dist/assets/guildStore-D5pntH7c.js 791 B
packages/frontend/dist/assets/index-BvzPtzfc.js 11.7 kB
packages/frontend/dist/assets/index-DoLe_VYd.css 14.4 kB
packages/frontend/dist/assets/input-B8xLKRen.js 465 B
packages/frontend/dist/assets/label-9gyU3CvE.js 485 B
packages/frontend/dist/assets/LastFm-rG5GrbYY.js 1.74 kB
packages/frontend/dist/assets/Levels-DLo4t0Rt.js 2.65 kB
packages/frontend/dist/assets/Login-C5vaBplo.js 2.54 kB
packages/frontend/dist/assets/Lyrics-C6bZmDoy.js 1.33 kB
packages/frontend/dist/assets/Moderation-Ceej5Axm.js 3.87 kB
packages/frontend/dist/assets/Music-DwHM3KDy.js 6.17 kB
packages/frontend/dist/assets/MusicConfig-BkgUtXsR.js 1.62 kB
packages/frontend/dist/assets/PrivacyPolicy-BMYU-0jY.js 1.38 kB
packages/frontend/dist/assets/rbac-BN1DzQHh.js 152 B
packages/frontend/dist/assets/ReactionRoles-D-ubvLwR.js 1.9 kB
packages/frontend/dist/assets/rolldown-runtime-COnpUsM8.js 467 B
packages/frontend/dist/assets/scroll-area-C8T6RLOh.js 522 B
packages/frontend/dist/assets/SectionHeader-C0yb_DFu.js 385 B
packages/frontend/dist/assets/select-CHLbPSDw.js 1.23 kB
packages/frontend/dist/assets/ServerLogs-BtUuCMku.js 2.9 kB
packages/frontend/dist/assets/ServerSettings-4VsKJdYy.js 4.22 kB
packages/frontend/dist/assets/ServersPage-CLyZtYdk.js 2.95 kB
packages/frontend/dist/assets/Skeleton-Z9dBUKZD.js 236 B
packages/frontend/dist/assets/Starboard-xddi8bqd.js 2.09 kB
packages/frontend/dist/assets/StatTile-DI0Q4bgV.js 606 B
packages/frontend/dist/assets/switch-nhTZOPm5.js 543 B
packages/frontend/dist/assets/TermsOfService-SB9iOf3u.js 1.37 kB
packages/frontend/dist/assets/TrackHistory-Brg7jkn6.js 1.91 kB
packages/frontend/dist/assets/TwitchNotifications-BrtUbdEd.js 2.29 kB
packages/frontend/dist/assets/useGuildSelection-CNWhdqGT.js 488 B
packages/frontend/dist/assets/usePageMetadata-C6uKjjUo.js 325 B
packages/frontend/dist/assets/utils-ayWzbeKE.js 148 B
packages/frontend/dist/assets/vendor-forms-DmZCnE0r.js 25.4 kB
packages/frontend/dist/assets/vendor-radix-DyTfqI7p.js 33.5 kB
packages/frontend/dist/assets/vendor-react-DlFk5rSD.js 55.6 kB
packages/frontend/dist/assets/vendor-state-1dKZOhDf.js 22.2 kB
packages/frontend/dist/assets/vendor-ui-B32-89km.js 64.2 kB

compressed-size-action

- creates play/queryutils.ts with discord_unknown_interaction_code,
  isunknowninteractionerror, isurl, and resolvesearchengine
- removes duplicate utilities from playtop.ts and playskip.ts
- adds comprehensive execute logic tests for playtop and playskip
- increases coverage from 2 tests (command structure) to 12+ tests
  covering error cases, queue operations, and success scenarios
- seek.spec.ts already has full test coverage

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
packages/bot/src/functions/music/commands/playtop.spec.ts (1)

53-64: These assertions miss the actual playtop contract.

Right now the spec only checks builder shape. It won't catch regressions where execute() fails to queue the new track first or replies with the wrong result. Please cover the ordering behavior itself. As per coding guidelines, "Test behavior, not implementation details" and "Prefer unit tests for core logic; add integration tests at meaningful boundaries".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playtop.spec.ts` around lines 53 -
64, The test currently only asserts the playTopCommand shape; add behavioral
tests for playTopCommand.execute to ensure it enqueues the new track at the
front and replies with the correct result: mock/stub the player/queue and a
message interaction, call playTopCommand.execute with an existing queue (and
with an empty queue) and assert that the new track appears at index 0 in the
queue and that interaction.reply (or the command's reply method) is called with
the expected success message; reference playTopCommand.execute and the
queue/player mock to locate where to inject these assertions.
packages/bot/src/functions/music/commands/replay.spec.ts (1)

59-63: The no current track case is exercising an impl-only state.

Lines 59-63 make requireCurrentTrack succeed globally, so Lines 97-105 only pass because the test bypasses the real validator contract. That leaves the meaningful branches here under-covered. I’d switch this case to assert the early return when requireCurrentTrack is false, and add a paused-queue case once the command semantics are settled. As per coding guidelines, "Test behavior, not implementation details" and "Prefer unit tests for core logic; add integration tests at meaningful boundaries".

Also applies to: 97-105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/replay.spec.ts` around lines 59 -
63, The test currently forces requireCurrentTrack to succeed globally
(requireCurrentTrackMock.mockResolvedValue(true)), which hides the "no current
track" branch; change the specific "no current track" spec to set
requireCurrentTrackMock.mockResolvedValue(false) (while keeping other validators
as needed) and assert that the command returns/early-exits (e.g., checks for the
expected reply or no-op) instead of relying on implementation state; also add a
separate spec that simulates a paused queue by adjusting requireIsPlayingMock
(or the queue mock) to reflect a paused state and assert the paused-queue
behavior once command semantics are finalized.
packages/bot/src/functions/music/commands/playskip.spec.ts (1)

53-64: These tests only pin slash metadata, not playskip behavior.

Lines 53-64 will still pass if execute() stops inserting the new track at the front or stops skipping immediately, which are the risky parts of this feature. Please add at least one execution-path test that asserts the queue mutation and skip side effect. As per coding guidelines, "Test behavior, not implementation details" and "Prefer unit tests for core logic; add integration tests at meaningful boundaries".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playskip.spec.ts` around lines 53 -
64, Add an execution-path test in playskip.spec.ts that exercises
playSkipCommand.execute instead of only verifying slash metadata: invoke
playSkipCommand.execute with a mocked command context containing a "query"
option and a fake player/queue, stub the queue mutation and skip methods (e.g.,
the queue insertion function and the player's skip/next method), then assert
that the new track was inserted at the front of the queue (index 0) and that the
skip/next method was called once; use test doubles/spies to verify both the
queue mutation and the immediate skip side effect rather than inspecting
internal implementation details.
packages/bot/src/functions/music/commands/playtop.ts (2)

84-84: Use const instead of let for variables that are not reassigned.

result is never reassigned after initialization.

♻️ Suggested fix
-            let result = await client.player.play(voiceChannel, query, {
+            const result = await client.player.play(voiceChannel, query, {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playtop.ts` at line 84, The
variable declaration for result (assigned from client.player.play in playtop.ts)
uses let but is never reassigned; change its declaration to const to reflect
immutability—locate the assignment to result (let result = await
client.player.play(voiceChannel, query, { ... })) and replace let with const so
the variable is read-only.

16-34: Extract duplicated helper functions to a shared utility.

isUnknownInteractionError, isUrl, and resolveSearchEngine are duplicated across playtop.ts, playskip.ts, and play/index.ts. Additionally, play/index.ts has a more complete resolveSearchEngine that supports a provider parameter, while the other two only accept query. Extract these to a shared module (e.g., utils/music/searchEngine.ts or utils/general/discordErrors.ts) to follow DRY principles and enable reuse of the enhanced resolveSearchEngine signature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playtop.ts` around lines 16 - 34,
The helpers isUnknownInteractionError, isUrl, and resolveSearchEngine are
duplicated and should be extracted into a shared utility; create a new module
(e.g., utils/music/searchEngine or utils/general) that exports
isUnknownInteractionError, isUrl, and the enhanced resolveSearchEngine signature
(accepting query and optional provider like the version in play/index.ts),
replace the local definitions in playtop.ts (and similarly in playskip.ts and
play/index.ts) with imports from that module, and update call sites to use the
unified resolveSearchEngine(provider?, query?) API so all three files reuse the
single implementation.
packages/bot/src/functions/music/commands/skipto.ts (1)

52-52: Unnecessary optional chaining after validation.

queue is guaranteed to exist here since requireQueue would have returned early otherwise. The optional chaining queue?.node.skipTo is redundant and slightly obscures the control flow.

♻️ Suggested simplification
-        queue?.node.skipTo(targetIndex)
+        queue.node.skipTo(targetIndex)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/skipto.ts` at line 52, The optional
chaining on queue is unnecessary because requireQueue ensures queue exists;
replace the call queue?.node.skipTo(targetIndex) with a direct call
queue.node.skipTo(targetIndex) in skipto.ts (the code path after requireQueue)
to simplify control flow and remove the redundant ?. Keep the same targetIndex
and error handling unchanged.
packages/bot/src/functions/music/commands/playskip.ts (1)

83-83: Use const instead of let.

result is not reassigned.

♻️ Suggested fix
-            let result = await client.player.play(voiceChannel, query, {
+            const result = await client.player.play(voiceChannel, query, {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playskip.ts` at line 83, The
variable "result" declared in the playskip command is never reassigned; change
its declaration from "let result = await client.player.play(voiceChannel, query,
{ ... })" to use "const" instead to reflect immutability and prevent accidental
reassignment; update the declaration where "result" is created in playskip.ts
(the client.player.play call) to use const.
packages/bot/src/functions/music/commands/skipto.spec.ts (1)

60-122: Consider adding a test for queue validation failure.

The test suite covers voice channel validation failure (line 68-76) but doesn't test the early return when requireQueue returns false. Adding this case would ensure complete coverage of the validation paths.

it('returns early when queue validation fails', async () => {
    requireQueueMock.mockResolvedValue(false)
    const queue = createQueue([{ id: 'track-1' }])
    resolveGuildQueueMock.mockReturnValue({ queue })

    await skiptoCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

    expect(queue.node.skipTo).not.toHaveBeenCalled()
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/skipto.spec.ts` around lines 60 -
122, Add a new unit test that asserts the command returns early when queue
validation fails by mocking requireQueueMock to resolve false, calling
skiptoCommand.execute with a mocked interaction (use createInteraction) and a
resolved guild queue (resolveGuildQueueMock -> { queue } from createQueue), and
then asserting queue.node.skipTo was not called; target the same pattern used in
the existing voice-channel-failure test and reference requireQueueMock,
skiptoCommand.execute, resolveGuildQueueMock, createQueue, createInteraction,
and queue.node.skipTo when adding the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bot/src/functions/music/commands/play/index.ts`:
- Around line 38-49: The resolveSearchEngine function (and the related retry
logic handling provider-specific cases) currently falls through to other
providers when a provider was explicitly passed, causing duplicate retries
(e.g., provider='youtube' falling back to auto) and silent cross-provider
resolution; update resolveSearchEngine to return only the requested provider's
QueryType when provider is non-null/defined (no fall-through to default), and
modify the retry ladder logic that picks alternate engines (the code around the
other ranges noted) so cross-provider fallback happens only when provider is
omitted — alternatively ensure the retry picker skips engines already attempted
(track attempted QueryType values) to avoid reusing the same engine twice.

In `@packages/bot/src/functions/music/commands/replay.ts`:
- Around line 21-29: The requireIsPlaying() pre-check is blocking seek
operations while paused; remove the requireIsPlaying() guard from the replay
command (remove the call to requireIsPlaying(...) in replay.ts) and from the
seek command (remove the call to requireIsPlaying(...) in seek.ts) so that
queue.node.seek(...) can be called on paused queues; keep the existing
requireVoiceChannel(...), requireQueue(...), and requireCurrentTrack(...) checks
intact to validate context before calling queue.node.seek(…) and ensure behavior
matches the web handler's seek logic.

In `@packages/bot/src/functions/music/commands/seek.ts`:
- Around line 15-31: parseTimeToMs currently uses parseInt which allows trailing
junk (e.g., "1:30foo" or "90s") to be accepted; change parseTimeToMs to validate
the input exactly before converting by matching the whole string against strict
patterns (e.g., /^\d+$/ for seconds-only and /^\d+:\d{1,2}$/ for
minutes:seconds) and only then parse the numeric parts, ensuring seconds < 60
and all values are non-negative; update the function (parseTimeToMs) to return
null for any input that does not fully match those exact formats so no partial
parses are accepted.

In `@packages/bot/src/functions/music/commands/skipto.ts`:
- Around line 51-68: The code currently calls queue.node.skipTo(targetIndex)
before capturing the intended target track, causing
queue?.tracks.toArray?.()?.[targetIndex] to reference the wrong item; change the
order so you first read and store the intended target track into a local
variable (e.g., targetTrack) using queue?.tracks.toArray?.()?.[targetIndex] or
capturing queue.tracks.get(targetIndex) if available, then call
queue.node.skipTo(targetIndex), and finally use that stored targetTrack when
building the interactionReply/createSuccessEmbed response.

---

Nitpick comments:
In `@packages/bot/src/functions/music/commands/playskip.spec.ts`:
- Around line 53-64: Add an execution-path test in playskip.spec.ts that
exercises playSkipCommand.execute instead of only verifying slash metadata:
invoke playSkipCommand.execute with a mocked command context containing a
"query" option and a fake player/queue, stub the queue mutation and skip methods
(e.g., the queue insertion function and the player's skip/next method), then
assert that the new track was inserted at the front of the queue (index 0) and
that the skip/next method was called once; use test doubles/spies to verify both
the queue mutation and the immediate skip side effect rather than inspecting
internal implementation details.

In `@packages/bot/src/functions/music/commands/playskip.ts`:
- Line 83: The variable "result" declared in the playskip command is never
reassigned; change its declaration from "let result = await
client.player.play(voiceChannel, query, { ... })" to use "const" instead to
reflect immutability and prevent accidental reassignment; update the declaration
where "result" is created in playskip.ts (the client.player.play call) to use
const.

In `@packages/bot/src/functions/music/commands/playtop.spec.ts`:
- Around line 53-64: The test currently only asserts the playTopCommand shape;
add behavioral tests for playTopCommand.execute to ensure it enqueues the new
track at the front and replies with the correct result: mock/stub the
player/queue and a message interaction, call playTopCommand.execute with an
existing queue (and with an empty queue) and assert that the new track appears
at index 0 in the queue and that interaction.reply (or the command's reply
method) is called with the expected success message; reference
playTopCommand.execute and the queue/player mock to locate where to inject these
assertions.

In `@packages/bot/src/functions/music/commands/playtop.ts`:
- Line 84: The variable declaration for result (assigned from client.player.play
in playtop.ts) uses let but is never reassigned; change its declaration to const
to reflect immutability—locate the assignment to result (let result = await
client.player.play(voiceChannel, query, { ... })) and replace let with const so
the variable is read-only.
- Around line 16-34: The helpers isUnknownInteractionError, isUrl, and
resolveSearchEngine are duplicated and should be extracted into a shared
utility; create a new module (e.g., utils/music/searchEngine or utils/general)
that exports isUnknownInteractionError, isUrl, and the enhanced
resolveSearchEngine signature (accepting query and optional provider like the
version in play/index.ts), replace the local definitions in playtop.ts (and
similarly in playskip.ts and play/index.ts) with imports from that module, and
update call sites to use the unified resolveSearchEngine(provider?, query?) API
so all three files reuse the single implementation.

In `@packages/bot/src/functions/music/commands/replay.spec.ts`:
- Around line 59-63: The test currently forces requireCurrentTrack to succeed
globally (requireCurrentTrackMock.mockResolvedValue(true)), which hides the "no
current track" branch; change the specific "no current track" spec to set
requireCurrentTrackMock.mockResolvedValue(false) (while keeping other validators
as needed) and assert that the command returns/early-exits (e.g., checks for the
expected reply or no-op) instead of relying on implementation state; also add a
separate spec that simulates a paused queue by adjusting requireIsPlayingMock
(or the queue mock) to reflect a paused state and assert the paused-queue
behavior once command semantics are finalized.

In `@packages/bot/src/functions/music/commands/skipto.spec.ts`:
- Around line 60-122: Add a new unit test that asserts the command returns early
when queue validation fails by mocking requireQueueMock to resolve false,
calling skiptoCommand.execute with a mocked interaction (use createInteraction)
and a resolved guild queue (resolveGuildQueueMock -> { queue } from
createQueue), and then asserting queue.node.skipTo was not called; target the
same pattern used in the existing voice-channel-failure test and reference
requireQueueMock, skiptoCommand.execute, resolveGuildQueueMock, createQueue,
createInteraction, and queue.node.skipTo when adding the test.

In `@packages/bot/src/functions/music/commands/skipto.ts`:
- Line 52: The optional chaining on queue is unnecessary because requireQueue
ensures queue exists; replace the call queue?.node.skipTo(targetIndex) with a
direct call queue.node.skipTo(targetIndex) in skipto.ts (the code path after
requireQueue) to simplify control flow and remove the redundant ?. Keep the same
targetIndex and error handling unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 213bea6c-7ec7-4284-b3a8-471308495744

📥 Commits

Reviewing files that changed from the base of the PR and between 5cb5ec1 and 5871b2e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (15)
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/queueResolverWiring.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/resume.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/seek.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/skipto.ts
💤 Files with no reviewable changes (2)
  • packages/bot/src/functions/music/commands/queueResolverWiring.spec.ts
  • packages/bot/src/functions/music/commands/resume.spec.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Quality Gates
  • GitHub Check: SonarCloud Scan
  • GitHub Check: compressed-size
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{js,jsx,ts,tsx,vue,html}

📄 CodeRabbit inference engine (.cursor/rules/accessibility-openness.mdc)

Provide accessible UI components using semantic HTML and ARIA attributes where necessary

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/dependency-injection.mdc)

**/*.{ts,tsx,js,jsx}: Prefer constructor injection for classes that require dependencies
Avoid global mutable singletons unless necessary
Use explicit interfaces for external dependencies to make testing easier

**/*.{ts,tsx,js,jsx}: Include required references in PRs/code for non-trivial logic: TypeScript (official docs), MDN (JavaScript reference), and official docs for any runtime/framework/libraries used (e.g., Node.js, React) as applicable.
Before assuming behavior of an API, include the doc link and a ≤25-word quote when the change relies on it.

**/*.{ts,tsx,js,jsx}: Prefer named exports for clear usage and easier refactors in TypeScript/JavaScript
Keep import order consistent: external first, then internal modules
Remove dead code and unused imports

**/*.{ts,tsx,js,jsx}: Use PascalCase naming convention for React/UI components
Use camelCase naming convention for variables and functions
Use UPPER_SNAKE_CASE naming convention for constants
Maintain consistent import grouping and ordering within the project, keeping third-party imports separate from local imports
For external data sources (HTTP, database), always validate and sanitize input using type guards or schema validators

Implement TypeScript typecheck and linter in CI quality checks

**/*.{ts,tsx,js,jsx}: Use TypeScript for enhanced type safety
Implement error handling and error logging
Avoid commenting code unless extremely necessary - code should explain itself with descriptive names
Leave NO todos, placeholders or missing pieces in the code
Variables and functions must use camelCase
Constants must use UPPER_SNAKE_CASE
Use arrow functions for methods and computed properties
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
Maintain consistent import grouping/order: external imports first, then internal modules
Use named exports for clear usage and easier refactors
Always validate and sanitize external data (HTTP, DB) at the boundary using type guards ...

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{js,jsx,ts,tsx}: Never throw strings. Throw Error (or typed subclasses) with descriptive messages
Include causal error as cause when available for better debugging
Define clear, stable error codes (e.g., ERR_AUTH_EXPIRED, ERR_NETWORK_TIMEOUT)
Provide optional metadata (e.g., details, retryable, status, correlationId) in error objects
Use domain error classes per area (e.g., AuthenticationError, ValidationError, NetworkError)
Log errors with structure (message, code, stack, cause, correlationId, user context where appropriate)
Mark retryable vs nonRetryable errors where helpful for operations
Set timeouts and handle aborts/cancellations; avoid dangling requests in API/network code
Implement backoff for transient failures; avoid infinite retries
Map HTTP status → domain errors; 4xx vs 5xx behave differently (e.g., retry for 5xx/network)

**/*.{js,jsx,ts,tsx}: Use functional components with hooks in React/React Native. Avoid class components.
Keep components focused on a single responsibility; extract complex logic into custom hooks.
Keep state local when possible. Use Context/Zustand/Redux only when necessary for state management.
If props or state traverse more than 3 levels, consider using context or a feature-scoped store instead of prop drilling.
Use performance optimization techniques: React.memo, useMemo, useCallback, Suspense (web), and virtualization for long lists; avoid unnecessary re-renders.
Web accessibility: use semantic HTML, labels, focus management, keyboard navigation, and aria-* attributes as needed.
React Native accessibility: use accessibility props (accessible, accessibilityLabel), proper roles and labels.
Identify and extract repetitive UI components proactively to components/ with clear props and minimal coupling.
Web styles: prefer co-located styles or design system tokens; avoid global style leakage.
React Native styles: prefer StyleSheet.create, design tokens, and theme providers; avoid in...

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/index.ts

📄 CodeRabbit inference engine (.cursor/rules/pattern.mdc)

Use index.ts only to re-export a small, intentional surface per module

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pattern.mdc)

Introduce interfaces at module boundaries to enable testing and substitutions

**/*.{ts,tsx}: Avoid using any type in TypeScript. If unavoidable, use unknown with type guards and justify with a code comment
Prefer interface for defining public object shapes in TypeScript, use type for unions and utility types
Use TypeScript utility types such as Partial, Pick, Omit, Readonly, and Record when appropriate
Use I{Name} naming convention for interfaces in TypeScript
Use T{Name} naming convention for type aliases and utility types in TypeScript

**/*.{ts,tsx}: Prefer types over interfaces for most cases
Don't ever use any - type safety always
Avoid enums; use const objects instead
For complex types, create a separate file to declare them and import them
Avoid using any type; if unavoidable, use unknown with type guards and justify with code comment
Prefer interface for public API shapes; use type for unions and utility types
Use TypeScript utility types (Partial, Pick, Omit, Readonly, Record)

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/documentation.mdc)

**/*.{js,ts,tsx,jsx}: Minimize comments in code; explain the 'why' when non-obvious, let code express the 'what' through clear naming
Document trade-offs briefly when deviating from ideal patterns

**/*.{js,ts,tsx,jsx}: Store secrets, ports, and hosts in environment variables (.env, .env.example) and never hardcode them
Avoid redundant or decorative AI comments; code should be self-explanatory and only commented when logic is non-obvious; prefer refactoring over lengthy comments

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/src/functions/*/commands/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/lucky-discord-bot.mdc)

packages/bot/src/functions/*/commands/**/*.{ts,tsx}: Command model must include data (slash builder), execute, and category properties exported from packages/bot/src/models/Command.ts
Use @discordjs/builders for building the data (SlashCommandBuilder) in command definitions
Command execute function must receive { interaction, client } parameters from CommandExecuteParams type
Use interactionReply and createUserFriendlyError utilities from @lucky/shared/general utils for command replies and error handling
Use existing validators from packages/bot/src/utils/command/ for voice channel, queue, and guild validations in commands

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/lucky-discord-bot.mdc)

packages/bot/**/*.{ts,tsx}: Use useMainPlayer() from discord-player to access the player instance; do not instantiate player directly
Do not duplicate queue or player state outside Discord Player; use shared services from @lucky/shared for persistent data like track history and session information
Use errorLog and debugLog from @lucky/shared/utils for logging throughout the bot package
Use embed and reply utilities from @lucky/shared for consistent message formatting and error sanitization across the bot
Use services from @lucky/shared (DatabaseService, Redis client) for database and cache access; do not instantiate Prisma or Redis directly in the bot package

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/**

📄 CodeRabbit inference engine (.cursor/rules/lucky-project.mdc)

The bot package depends on shared and contains Discord bot commands and player handlers using Discord.js and Discord Player

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/*.{js,mjs,ts,mts}

📄 CodeRabbit inference engine (.cursor/rules/lucky-project.mdc)

Use Node.js version ≥22 with ESM (ECMAScript modules) only; no CommonJS

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/src/functions/{general,music,download}/commands/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

packages/bot/src/functions/{general,music,download}/commands/**/*.ts: Apply .cursor/rules/lucky-discord-bot.mdc rules for Discord bot commands and player implementation
Use .cursor/skills/discord-commands/SKILL.md for implementing slash commands

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/src/functions/music/commands/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use .cursor/skills/music-queue-player/SKILL.md for play, queue, skip, volume commands and player lifecycle management

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/src/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use @lucky/shared for database, Redis, logging, and embed utilities instead of implementing them locally

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
packages/bot/src/functions/music/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use existing voice/queue/guild validators before manipulating player or queue state

Files:

  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/pause.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/replay.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/skipto.ts
  • packages/bot/src/functions/music/commands/seek.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Test behavior, not implementation. Prefer Testing Library utilities for testing React/React Native components.
For React Native tests: mock native modules and test component interactions and accessibility labels.

Files:

  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/pattern.mdc)

**/*.{test,spec}.{ts,tsx,js,jsx}: Test behavior, not implementation details
Prefer unit tests for core logic; add integration tests at meaningful boundaries

Files:

  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/testing-quality.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Use Jest + a React testing library for unit and component tests as applicable
Test behavior, not implementation details

Files:

  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
**/*.{spec,test}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/quality.mdc)

**/*.{spec,test}.{ts,tsx,js,jsx}: Use Jest for unit and integration tests
Test behavior, not implementation details
Run unit, integration tests, and coverage report in CI quality checks

Files:

  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/quality.mdc)

Unit tests must use naming convention *.spec.ts

Files:

  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/replay.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/skipto.spec.ts
  • packages/bot/src/functions/music/commands/pause.spec.ts
  • packages/bot/src/functions/music/commands/seek.spec.ts
🔇 Additional comments (7)
packages/bot/src/functions/music/commands/pause.ts (1)

25-35: Nice toggle-state handling.

Using the pre-toggle isPaused value for both the node action and the reply label keeps the embed text aligned with what actually happened.

Also applies to: 51-52

packages/bot/src/functions/music/commands/pause.spec.ts (1)

84-128: Nice coverage of both toggle directions.

The paused/playing plus current-track/no-track matrix should catch most regressions in the new pause/resume semantics.

packages/bot/src/functions/music/commands/seek.ts (1)

57-59: Remove requireIsPlaying() to allow seeking on paused tracks.

Line 59 blocks paused queues from seeking. Since the /pause command allows toggling on any queue state and the web handler seeks without an isPlaying check, paused queues with a currentTrack should be seekable. Remove this validation to enable the pause → seek → resume flow.

packages/bot/src/functions/music/commands/playskip.ts (2)

17-33: Duplicate helper functions (same issue as playtop.ts).

These helper functions are identical to those in playtop.ts. See the comment on playtop.ts lines 16-34 for the recommended refactor to extract these to a shared utility.


109-114: Queue manipulation and skip logic looks correct.

The sequence of removing the track from its default position, inserting at index 0, then calling skip() should correctly play the newly added track immediately. The conditional check ensures this only happens when there are existing tracks in the queue.

packages/bot/src/functions/music/commands/playtop.ts (1)

110-114: LGTM on queue manipulation logic.

The approach of removing the track from its default position and reinserting at index 0 correctly places the new track at the front of the queue. The conditional tracks.length > 0 appropriately handles the edge case of an empty queue.

packages/bot/src/functions/music/commands/seek.spec.ts (1)

61-138: Good test coverage with comprehensive edge cases.

The test suite effectively covers the key behaviors: time format parsing (mm:ss and ss), validation errors (invalid format, out of range, non-seekable tracks), and early return on voice channel validation failure. The mock setup follows the established pattern used across other music command tests.

Consider adding tests for the other validation failures (requireQueue, requireCurrentTrack, requireIsPlaying) for completeness, though the current coverage captures the most critical paths.

Comment on lines +38 to +49
function resolveSearchEngine(query: string, provider?: string | null): QueryType {
if (isUrl(query)) return QueryType.AUTO
return QueryType.SPOTIFY_SEARCH

switch (provider) {
case 'youtube':
return QueryType.YOUTUBE_SEARCH
case 'soundcloud':
return QueryType.SOUNDCLOUD_SEARCH
case 'spotify':
default:
return QueryType.SPOTIFY_SEARCH
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't fall through to other providers when provider was explicitly chosen.

With the new option, provider becomes user intent. The existing retry ladder still behaves like “Spotify → YouTube → auto”, so provider='youtube' retries the same engine twice and provider='soundcloud' can silently resolve from a different source. I’d only use cross-provider fallback when provider is omitted, or skip search engines that were already attempted.

Also applies to: 79-89, 121-147

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/play/index.ts` around lines 38 -
49, The resolveSearchEngine function (and the related retry logic handling
provider-specific cases) currently falls through to other providers when a
provider was explicitly passed, causing duplicate retries (e.g.,
provider='youtube' falling back to auto) and silent cross-provider resolution;
update resolveSearchEngine to return only the requested provider's QueryType
when provider is non-null/defined (no fall-through to default), and modify the
retry ladder logic that picks alternate engines (the code around the other
ranges noted) so cross-provider fallback happens only when provider is omitted —
alternatively ensure the retry picker skips engines already attempted (track
attempted QueryType values) to avoid reusing the same engine twice.

Comment on lines +21 to +29
if (!(await requireVoiceChannel(interaction))) return

const { queue } = resolveGuildQueue(client, interaction.guildId ?? '')

if (!(await requireQueue(queue, interaction))) return
if (!(await requireCurrentTrack(queue, interaction))) return
if (!(await requireIsPlaying(queue, interaction))) return

if (queue !== null && queue !== undefined && !queue.node.isPaused()) {
await interactionReply({
interaction,
content: {
embeds: [
createWarningEmbed(
'Already playing',
'▶️ Music is already playing.',
),
],
ephemeral: true,
},
})
return
}

queue?.node.resume()
queue?.node.seek(0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the replay.ts file
find . -type f -name "replay.ts" | head -5

Repository: LucasSantana-Dev/Lucky

Length of output: 119


🏁 Script executed:

# Find the webMusic command handler file
find . -type f -path "*webMusic/commandHandlers.ts"

Repository: LucasSantana-Dev/Lucky

Length of output: 121


🏁 Script executed:

# Check for discord-player documentation or type definitions to understand seek behavior on paused queues
find . -type f -name "*.ts" -o -name "*.d.ts" | xargs rg -l "discord-player" | head -10

Repository: LucasSantana-Dev/Lucky

Length of output: 579


🏁 Script executed:

# Read replay.ts to understand the full structure
cat -n ./packages/bot/src/functions/music/commands/replay.ts | head -40

Repository: LucasSantana-Dev/Lucky

Length of output: 1985


🏁 Script executed:

# Read webMusic handler to see seek without paused gate
cat -n ./packages/bot/src/handlers/webMusic/commandHandlers.ts | sed -n '155,180p'

Repository: LucasSantana-Dev/Lucky

Length of output: 851


🏁 Script executed:

# Find and read the commandValidations file to see what requireIsPlaying does
find . -type f -path "*utils/command/commandValidations*"

Repository: LucasSantana-Dev/Lucky

Length of output: 182


🏁 Script executed:

# Read the validator implementations
cat -n ./packages/bot/src/utils/command/commandValidations.ts | head -100

Repository: LucasSantana-Dev/Lucky

Length of output: 3627


🏁 Script executed:

# Continue reading to find requireIsPlaying
cat -n ./packages/bot/src/utils/command/commandValidations.ts | sed -n '100,150p'

Repository: LucasSantana-Dev/Lucky

Length of output: 1795


🏁 Script executed:

# Check if there's a pause/resume handler or logic to understand queue state
rg -A 5 -B 2 "isPlaying|seek" ./packages/bot/src/handlers/webMusic/commandHandlers.ts | head -50

Repository: LucasSantana-Dev/Lucky

Length of output: 526


🏁 Script executed:

# Look for pause/resume command handlers
rg -l "pause|resume" ./packages/bot/src/functions/music/commands/*.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 248


🏁 Script executed:

# Read the pause command to understand pause/resume flow
cat -n ./packages/bot/src/functions/music/commands/pause.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 2361


🏁 Script executed:

# Search for how discord-player queue.node.seek behaves on paused queues
# Check the discord-player package or type definitions if available
find . -type f -name "*.json" | xargs grep -l "discord-player" | head -3

Repository: LucasSantana-Dev/Lucky

Length of output: 128


🏁 Script executed:

# Check package.json to see discord-player version
cat ./packages/bot/package.json | grep -A 2 -B 2 "discord-player"

Repository: LucasSantana-Dev/Lucky

Length of output: 438


🏁 Script executed:

# Search for other uses of seek in the codebase to understand expected behavior
rg "\.seek\(" --type ts -B 2 -A 2

Repository: LucasSantana-Dev/Lucky

Length of output: 3530


🏁 Script executed:

# Read seek.ts to see what validations it uses
cat -n ./packages/bot/src/functions/music/commands/seek.ts | head -50

Repository: LucasSantana-Dev/Lucky

Length of output: 2223


🏁 Script executed:

# Continue reading seek.ts to see execute function
cat -n ./packages/bot/src/functions/music/commands/seek.ts | sed -n '50,85p'

Repository: LucasSantana-Dev/Lucky

Length of output: 1578


Both /replay and /seek are blocked in paused state.

Both commands use requireIsPlaying() before calling queue.node.seek(), rejecting paused queues. This breaks the pause → replay workflow and diverges from the web handler (packages/bot/src/handlers/webMusic/commandHandlers.ts:166-173), which allows seek without this gate. Discord Player's seek() operation should work on paused queues (it only changes position, not play state). Remove requireIsPlaying() from both replay.ts (line 27) and seek.ts (line 59).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/replay.ts` around lines 21 - 29,
The requireIsPlaying() pre-check is blocking seek operations while paused;
remove the requireIsPlaying() guard from the replay command (remove the call to
requireIsPlaying(...) in replay.ts) and from the seek command (remove the call
to requireIsPlaying(...) in seek.ts) so that queue.node.seek(...) can be called
on paused queues; keep the existing requireVoiceChannel(...), requireQueue(...),
and requireCurrentTrack(...) checks intact to validate context before calling
queue.node.seek(…) and ensure behavior matches the web handler's seek logic.

Comment on lines +15 to +31
function parseTimeToMs(timeStr: string): number | null {
const parts = timeStr.split(':')

if (parts.length === 1) {
const seconds = parseInt(parts[0], 10)
return !isNaN(seconds) && seconds >= 0 ? seconds * 1000 : null
}

if (parts.length === 2) {
const minutes = parseInt(parts[0], 10)
const seconds = parseInt(parts[1], 10)
return !isNaN(minutes) && !isNaN(seconds) && minutes >= 0 && seconds >= 0 && seconds < 60
? (minutes * 60 + seconds) * 1000
: null
}

return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reject partial parses in parseTimeToMs().

parseInt() accepts trailing junk, so inputs like 1:30foo and 90s currently pass validation and seek to a value the user did not actually enter. This should do an exact format check before converting.

Possible fix
 function parseTimeToMs(timeStr: string): number | null {
-    const parts = timeStr.split(':')
-
-    if (parts.length === 1) {
-        const seconds = parseInt(parts[0], 10)
-        return !isNaN(seconds) && seconds >= 0 ? seconds * 1000 : null
-    }
-
-    if (parts.length === 2) {
-        const minutes = parseInt(parts[0], 10)
-        const seconds = parseInt(parts[1], 10)
-        return !isNaN(minutes) && !isNaN(seconds) && minutes >= 0 && seconds >= 0 && seconds < 60
-            ? (minutes * 60 + seconds) * 1000
-            : null
-    }
-
-    return null
+    const normalized = timeStr.trim()
+    if (/^\d+$/.test(normalized)) return Number(normalized) * 1000
+
+    const match = normalized.match(/^(\d+):(\d{1,2})$/)
+    if (!match) return null
+
+    const minutes = Number(match[1])
+    const seconds = Number(match[2])
+    return seconds < 60 ? (minutes * 60 + seconds) * 1000 : null
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function parseTimeToMs(timeStr: string): number | null {
const parts = timeStr.split(':')
if (parts.length === 1) {
const seconds = parseInt(parts[0], 10)
return !isNaN(seconds) && seconds >= 0 ? seconds * 1000 : null
}
if (parts.length === 2) {
const minutes = parseInt(parts[0], 10)
const seconds = parseInt(parts[1], 10)
return !isNaN(minutes) && !isNaN(seconds) && minutes >= 0 && seconds >= 0 && seconds < 60
? (minutes * 60 + seconds) * 1000
: null
}
return null
function parseTimeToMs(timeStr: string): number | null {
const normalized = timeStr.trim()
if (/^\d+$/.test(normalized)) return Number(normalized) * 1000
const match = normalized.match(/^(\d+):(\d{1,2})$/)
if (!match) return null
const minutes = Number(match[1])
const seconds = Number(match[2])
return seconds < 60 ? (minutes * 60 + seconds) * 1000 : null
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/seek.ts` around lines 15 - 31,
parseTimeToMs currently uses parseInt which allows trailing junk (e.g.,
"1:30foo" or "90s") to be accepted; change parseTimeToMs to validate the input
exactly before converting by matching the whole string against strict patterns
(e.g., /^\d+$/ for seconds-only and /^\d+:\d{1,2}$/ for minutes:seconds) and
only then parse the numeric parts, ensuring seconds < 60 and all values are
non-negative; update the function (parseTimeToMs) to return null for any input
that does not fully match those exact formats so no partial parses are accepted.

Comment on lines +51 to +68
const targetIndex = position - 1
queue?.node.skipTo(targetIndex)

const targetTrack = queue?.tracks.toArray?.()?.[targetIndex]
if (!targetTrack) {
await interactionReply({
interaction,
content: {
embeds: [
createSuccessEmbed(
'⏭️ Skipped',
`Skipped to position ${position}.`,
),
],
},
})
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

discord-player node.skipTo behavior queue tracks after skip

💡 Result:

In discord-player, queue.node.skipTo(track) skips to the specified track (TrackResolvable: Track, string ID, or number index), removing all tracks before it from the queue. Tracks after the target remain intact and will play sequentially afterward. Example from docs: // Skip to track at index 5, removing tracks 0-4 if (queue.node.skipTo(5)) { await interaction.reply('Skipped to track 6!'); } The target track becomes the new current track, and the queue continues with subsequent tracks. This differs from queue.node.jump(track), which moves the target to the front without removing preceding tracks. No reported bugs with skipTo behavior in recent versions (as of 2026). Past issues were with skip or jump.

Citations:


Capture target track before skipTo to prevent retrieving the wrong track.

After queue.node.skipTo(targetIndex) executes, the queue state changes—tracks preceding the target are removed, and the target track becomes currentTrack. Accessing queue.tracks.toArray()[targetIndex] after the skip will return a different track (or undefined) because the array has shifted. The target track must be captured before the skip call.

Proposed fix
         const targetIndex = position - 1
+        const targetTrack = queue?.tracks.toArray?.()?.[targetIndex]
         queue?.node.skipTo(targetIndex)
 
-        const targetTrack = queue?.tracks.toArray?.()?.[targetIndex]
         if (!targetTrack) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const targetIndex = position - 1
queue?.node.skipTo(targetIndex)
const targetTrack = queue?.tracks.toArray?.()?.[targetIndex]
if (!targetTrack) {
await interactionReply({
interaction,
content: {
embeds: [
createSuccessEmbed(
'⏭️ Skipped',
`Skipped to position ${position}.`,
),
],
},
})
return
}
const targetIndex = position - 1
const targetTrack = queue?.tracks.toArray?.()?.[targetIndex]
queue?.node.skipTo(targetIndex)
if (!targetTrack) {
await interactionReply({
interaction,
content: {
embeds: [
createSuccessEmbed(
'⏭️ Skipped',
`Skipped to position ${position}.`,
),
],
},
})
return
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/skipto.ts` around lines 51 - 68,
The code currently calls queue.node.skipTo(targetIndex) before capturing the
intended target track, causing queue?.tracks.toArray?.()?.[targetIndex] to
reference the wrong item; change the order so you first read and store the
intended target track into a local variable (e.g., targetTrack) using
queue?.tracks.toArray?.()?.[targetIndex] or capturing
queue.tracks.get(targetIndex) if available, then call
queue.node.skipTo(targetIndex), and finally use that stored targetTrack when
building the interactionReply/createSuccessEmbed response.

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/bot/src/functions/music/commands/playtop.spec.ts (1)

101-101: Reduce any usage in test helpers/invocations

These casts/types hide contract drift between command execute params and mocks. Prefer typed helper aliases (unknown + narrow, or explicit test-local types) instead of any.

As per coding guidelines, "Don't ever use any - type safety always".

Also applies to: 109-109, 124-124, 142-142, 167-167, 188-188, 202-202, 231-231

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playtop.spec.ts` at line 101, The
tests use casts to any (e.g., the options array lookup that assigns const
queryOption = options.find((opt: any) => opt.name === 'query')), which hides
type drift; replace those any usages by declaring a small test-local type for
the option shape (e.g., type TestOption = { name: string; value?: string } or
use unknown and narrow) and use that type in the find callback and other similar
spots (lines referencing options, queryOption, etc.) so the test
helpers/assertions are statically typed and you avoid any; update all
occurrences flagged (around the queryOption and the other listed lines) to use
the test-local type or unknown+type-guard narrowing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bot/src/functions/music/commands/play/queryUtils.ts`:
- Around line 55-59: Replace the direct call to interaction.reply in the guild
check with the shared utilities: use interactionReply(...) instead of
interaction.reply and build the message with createUserFriendlyError (import
from `@lucky/shared/general`) so replies go through centralized handling; locate
the guild check in queryUtils.ts (the block using interaction.guildId and
createErrorEmbed) and swap to interactionReply(interaction,
createUserFriendlyError(...)) ensuring the function imports are added/updated.
- Around line 63-67: The code casts interaction.member to GuildMember and
accesses member.voice.channel after calling requireVoiceChannel, but
requireVoiceChannel only returns boolean and does not narrow types; add an
explicit guard to check interaction.member is present before casting (e.g., if
(!interaction.member) return) or change requireVoiceChannel into a proper type
guard that narrows interaction.member to GuildMember so the subsequent access to
member.voice.channel (voiceChannel) is safe; update the code paths around
requireVoiceChannel, interaction.member, and any usage of voiceChannel
accordingly.

In `@packages/bot/src/functions/music/commands/playtop.spec.ts`:
- Line 216: The test title in playtop.spec.ts is misleading: rename the it(...)
description from "shows nowPlaying embed on success" to reflect the actual
assertion checking { kind: 'addedToQueue' } (e.g., "shows addedToQueue embed on
success" or similar). Locate the failing test's it(...) block in the
playtop.spec.ts file and update the test name string to match the asserted
behavior (reference the assertion that expects kind: 'addedToQueue').

---

Nitpick comments:
In `@packages/bot/src/functions/music/commands/playtop.spec.ts`:
- Line 101: The tests use casts to any (e.g., the options array lookup that
assigns const queryOption = options.find((opt: any) => opt.name === 'query')),
which hides type drift; replace those any usages by declaring a small test-local
type for the option shape (e.g., type TestOption = { name: string; value?:
string } or use unknown and narrow) and use that type in the find callback and
other similar spots (lines referencing options, queryOption, etc.) so the test
helpers/assertions are statically typed and you avoid any; update all
occurrences flagged (around the queryOption and the other listed lines) to use
the test-local type or unknown+type-guard narrowing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: efc5e7e8-4b0e-421d-b278-2c82728de243

📥 Commits

Reviewing files that changed from the base of the PR and between 5871b2e and 893c909.

📒 Files selected for processing (6)
  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/bot/src/functions/music/commands/playskip.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/playtop.ts
  • packages/bot/src/functions/music/commands/play/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: SonarCloud Scan
  • GitHub Check: compressed-size
  • GitHub Check: Quality Gates
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{js,jsx,ts,tsx,vue,html}

📄 CodeRabbit inference engine (.cursor/rules/accessibility-openness.mdc)

Provide accessible UI components using semantic HTML and ARIA attributes where necessary

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/dependency-injection.mdc)

**/*.{ts,tsx,js,jsx}: Prefer constructor injection for classes that require dependencies
Avoid global mutable singletons unless necessary
Use explicit interfaces for external dependencies to make testing easier

**/*.{ts,tsx,js,jsx}: Include required references in PRs/code for non-trivial logic: TypeScript (official docs), MDN (JavaScript reference), and official docs for any runtime/framework/libraries used (e.g., Node.js, React) as applicable.
Before assuming behavior of an API, include the doc link and a ≤25-word quote when the change relies on it.

**/*.{ts,tsx,js,jsx}: Prefer named exports for clear usage and easier refactors in TypeScript/JavaScript
Keep import order consistent: external first, then internal modules
Remove dead code and unused imports

**/*.{ts,tsx,js,jsx}: Use PascalCase naming convention for React/UI components
Use camelCase naming convention for variables and functions
Use UPPER_SNAKE_CASE naming convention for constants
Maintain consistent import grouping and ordering within the project, keeping third-party imports separate from local imports
For external data sources (HTTP, database), always validate and sanitize input using type guards or schema validators

Implement TypeScript typecheck and linter in CI quality checks

**/*.{ts,tsx,js,jsx}: Use TypeScript for enhanced type safety
Implement error handling and error logging
Avoid commenting code unless extremely necessary - code should explain itself with descriptive names
Leave NO todos, placeholders or missing pieces in the code
Variables and functions must use camelCase
Constants must use UPPER_SNAKE_CASE
Use arrow functions for methods and computed properties
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
Maintain consistent import grouping/order: external imports first, then internal modules
Use named exports for clear usage and easier refactors
Always validate and sanitize external data (HTTP, DB) at the boundary using type guards ...

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

**/*.{js,jsx,ts,tsx}: Never throw strings. Throw Error (or typed subclasses) with descriptive messages
Include causal error as cause when available for better debugging
Define clear, stable error codes (e.g., ERR_AUTH_EXPIRED, ERR_NETWORK_TIMEOUT)
Provide optional metadata (e.g., details, retryable, status, correlationId) in error objects
Use domain error classes per area (e.g., AuthenticationError, ValidationError, NetworkError)
Log errors with structure (message, code, stack, cause, correlationId, user context where appropriate)
Mark retryable vs nonRetryable errors where helpful for operations
Set timeouts and handle aborts/cancellations; avoid dangling requests in API/network code
Implement backoff for transient failures; avoid infinite retries
Map HTTP status → domain errors; 4xx vs 5xx behave differently (e.g., retry for 5xx/network)

**/*.{js,jsx,ts,tsx}: Use functional components with hooks in React/React Native. Avoid class components.
Keep components focused on a single responsibility; extract complex logic into custom hooks.
Keep state local when possible. Use Context/Zustand/Redux only when necessary for state management.
If props or state traverse more than 3 levels, consider using context or a feature-scoped store instead of prop drilling.
Use performance optimization techniques: React.memo, useMemo, useCallback, Suspense (web), and virtualization for long lists; avoid unnecessary re-renders.
Web accessibility: use semantic HTML, labels, focus management, keyboard navigation, and aria-* attributes as needed.
React Native accessibility: use accessibility props (accessible, accessibilityLabel), proper roles and labels.
Identify and extract repetitive UI components proactively to components/ with clear props and minimal coupling.
Web styles: prefer co-located styles or design system tokens; avoid global style leakage.
React Native styles: prefer StyleSheet.create, design tokens, and theme providers; avoid in...

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend.mdc)

**/*.{test,spec}.{js,jsx,ts,tsx}: Test behavior, not implementation. Prefer Testing Library utilities for testing React/React Native components.
For React Native tests: mock native modules and test component interactions and accessibility labels.

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/pattern.mdc)

Introduce interfaces at module boundaries to enable testing and substitutions

**/*.{ts,tsx}: Avoid using any type in TypeScript. If unavoidable, use unknown with type guards and justify with a code comment
Prefer interface for defining public object shapes in TypeScript, use type for unions and utility types
Use TypeScript utility types such as Partial, Pick, Omit, Readonly, and Record when appropriate
Use I{Name} naming convention for interfaces in TypeScript
Use T{Name} naming convention for type aliases and utility types in TypeScript

**/*.{ts,tsx}: Prefer types over interfaces for most cases
Don't ever use any - type safety always
Avoid enums; use const objects instead
For complex types, create a separate file to declare them and import them
Avoid using any type; if unavoidable, use unknown with type guards and justify with code comment
Prefer interface for public API shapes; use type for unions and utility types
Use TypeScript utility types (Partial, Pick, Omit, Readonly, Record)

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/pattern.mdc)

**/*.{test,spec}.{ts,tsx,js,jsx}: Test behavior, not implementation details
Prefer unit tests for core logic; add integration tests at meaningful boundaries

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
**/*.{test,spec}.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/testing-quality.mdc)

**/*.{test,spec}.{js,ts,jsx,tsx}: Use Jest + a React testing library for unit and component tests as applicable
Test behavior, not implementation details

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/documentation.mdc)

**/*.{js,ts,tsx,jsx}: Minimize comments in code; explain the 'why' when non-obvious, let code express the 'what' through clear naming
Document trade-offs briefly when deviating from ideal patterns

**/*.{js,ts,tsx,jsx}: Store secrets, ports, and hosts in environment variables (.env, .env.example) and never hardcode them
Avoid redundant or decorative AI comments; code should be self-explanatory and only commented when logic is non-obvious; prefer refactoring over lengthy comments

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/src/functions/*/commands/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/lucky-discord-bot.mdc)

packages/bot/src/functions/*/commands/**/*.{ts,tsx}: Command model must include data (slash builder), execute, and category properties exported from packages/bot/src/models/Command.ts
Use @discordjs/builders for building the data (SlashCommandBuilder) in command definitions
Command execute function must receive { interaction, client } parameters from CommandExecuteParams type
Use interactionReply and createUserFriendlyError utilities from @lucky/shared/general utils for command replies and error handling
Use existing validators from packages/bot/src/utils/command/ for voice channel, queue, and guild validations in commands

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/lucky-discord-bot.mdc)

packages/bot/**/*.{ts,tsx}: Use useMainPlayer() from discord-player to access the player instance; do not instantiate player directly
Do not duplicate queue or player state outside Discord Player; use shared services from @lucky/shared for persistent data like track history and session information
Use errorLog and debugLog from @lucky/shared/utils for logging throughout the bot package
Use embed and reply utilities from @lucky/shared for consistent message formatting and error sanitization across the bot
Use services from @lucky/shared (DatabaseService, Redis client) for database and cache access; do not instantiate Prisma or Redis directly in the bot package

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/**

📄 CodeRabbit inference engine (.cursor/rules/lucky-project.mdc)

The bot package depends on shared and contains Discord bot commands and player handlers using Discord.js and Discord Player

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{js,mjs,ts,mts}

📄 CodeRabbit inference engine (.cursor/rules/lucky-project.mdc)

Use Node.js version ≥22 with ESM (ECMAScript modules) only; no CommonJS

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
**/*.{spec,test}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/quality.mdc)

**/*.{spec,test}.{ts,tsx,js,jsx}: Use Jest for unit and integration tests
Test behavior, not implementation details
Run unit, integration tests, and coverage report in CI quality checks

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/quality.mdc)

Unit tests must use naming convention *.spec.ts

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
packages/bot/src/functions/{general,music,download}/commands/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

packages/bot/src/functions/{general,music,download}/commands/**/*.ts: Apply .cursor/rules/lucky-discord-bot.mdc rules for Discord bot commands and player implementation
Use .cursor/skills/discord-commands/SKILL.md for implementing slash commands

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/src/functions/music/commands/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use .cursor/skills/music-queue-player/SKILL.md for play, queue, skip, volume commands and player lifecycle management

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/src/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use @lucky/shared for database, Redis, logging, and embed utilities instead of implementing them locally

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
packages/bot/src/functions/music/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/subagent-discord.mdc)

Use existing voice/queue/guild validators before manipulating player or queue state

Files:

  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
🔇 Additional comments (1)
packages/bot/src/functions/music/commands/play/queryUtils.ts (1)

96-98: No verification needed — queue.node.remove(track) is the official API in discord-player v7.1.0 and will work correctly. Both queue.node.remove() and queue.insertTrack() are supported methods per the discord-player documentation.

			> Likely an incorrect or invalid review comment.

Comment on lines +55 to +59
if (!interaction.guildId) {
await interaction.reply({
embeds: [createErrorEmbed('Error', 'This command can only be used in a server')],
ephemeral: true,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use interactionReply here to avoid uncaught reply failures

Line 56 directly uses interaction.reply(...), which bypasses your centralized reply/error handling path and can throw before the main try/catch flow.

🔧 Suggested fix
-        await interaction.reply({
-            embeds: [createErrorEmbed('Error', 'This command can only be used in a server')],
-            ephemeral: true,
-        })
+        await interactionReply({
+            interaction,
+            content: {
+                embeds: [createErrorEmbed('Error', 'This command can only be used in a server')],
+                ephemeral: true,
+            },
+        })

As per coding guidelines, "Use interactionReply and createUserFriendlyError utilities from @lucky/shared/general utils for command replies and error handling".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!interaction.guildId) {
await interaction.reply({
embeds: [createErrorEmbed('Error', 'This command can only be used in a server')],
ephemeral: true,
})
if (!interaction.guildId) {
await interactionReply({
interaction,
content: {
embeds: [createErrorEmbed('Error', 'This command can only be used in a server')],
ephemeral: true,
},
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/play/queryUtils.ts` around lines 55
- 59, Replace the direct call to interaction.reply in the guild check with the
shared utilities: use interactionReply(...) instead of interaction.reply and
build the message with createUserFriendlyError (import from
`@lucky/shared/general`) so replies go through centralized handling; locate the
guild check in queryUtils.ts (the block using interaction.guildId and
createErrorEmbed) and swap to interactionReply(interaction,
createUserFriendlyError(...)) ensuring the function imports are added/updated.

Comment on lines +63 to +67
const member = interaction.member as GuildMember
if (!(await requireVoiceChannel(interaction))) return

const voiceChannel = member.voice.channel!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether requireVoiceChannel narrows types or only returns boolean.
rg -n -A30 -B5 'requireVoiceChannel' packages/bot/src/utils/command

Repository: LucasSantana-Dev/Lucky

Length of output: 3564


Guard interaction.member against null/undefined before voice-channel access

requireVoiceChannel returns only a boolean and performs no type narrowing. Although it validates the voice channel exists internally, casting interaction.member to GuildMember on line 63 without first checking if member is null risks a runtime crash if the member is partial or API-shaped. Add an explicit guard like if (!interaction.member) return before line 63, or refactor requireVoiceChannel to return a type guard for safer narrowing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/play/queryUtils.ts` around lines 63
- 67, The code casts interaction.member to GuildMember and accesses
member.voice.channel after calling requireVoiceChannel, but requireVoiceChannel
only returns boolean and does not narrow types; add an explicit guard to check
interaction.member is present before casting (e.g., if (!interaction.member)
return) or change requireVoiceChannel into a proper type guard that narrows
interaction.member to GuildMember so the subsequent access to
member.voice.channel (voiceChannel) is safe; update the code paths around
requireVoiceChannel, interaction.member, and any usage of voiceChannel
accordingly.

expect(errorLog).toHaveBeenCalled()
})

it('shows nowPlaying embed on success', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test name does not match asserted behavior

Line 216 says “nowPlaying embed on success”, but the expectation at Line 235 checks kind: 'addedToQueue'. Rename the test to match what it validates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/functions/music/commands/playtop.spec.ts` at line 216, The
test title in playtop.spec.ts is misleading: rename the it(...) description from
"shows nowPlaying embed on success" to reflect the actual assertion checking {
kind: 'addedToQueue' } (e.g., "shows addedToQueue embed on success" or similar).
Locate the failing test's it(...) block in the playtop.spec.ts file and update
the test name string to match the asserted behavior (reference the assertion
that expects kind: 'addedToQueue').

@LucasSantana-Dev
LucasSantana-Dev merged commit 56be6e9 into main Apr 10, 2026
13 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the feat/music-quick-wins branch April 10, 2026 17:18
LucasSantana-Dev added a commit that referenced this pull request Apr 10, 2026
…me toggle (#522)

* feat(bot): add playtop, playskip, skipto, seek, replay commands and pause/resume toggle

* fix(bot): extract shared play utilities and improve test coverage

- creates play/queryutils.ts with discord_unknown_interaction_code,
  isunknowninteractionerror, isurl, and resolvesearchengine
- removes duplicate utilities from playtop.ts and playskip.ts
- adds comprehensive execute logic tests for playtop and playskip
- increases coverage from 2 tests (command structure) to 12+ tests
  covering error cases, queue operations, and success scenarios
- seek.spec.ts already has full test coverage

* refactor: extract shared play-at-top logic to reduce duplication
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
…me toggle (#522)

* feat(bot): add playtop, playskip, skipto, seek, replay commands and pause/resume toggle

* fix(bot): extract shared play utilities and improve test coverage

- creates play/queryutils.ts with discord_unknown_interaction_code,
  isunknowninteractionerror, isurl, and resolvesearchengine
- removes duplicate utilities from playtop.ts and playskip.ts
- adds comprehensive execute logic tests for playtop and playskip
- increases coverage from 2 tests (command structure) to 12+ tests
  covering error cases, queue operations, and success scenarios
- seek.spec.ts already has full test coverage

* refactor: extract shared play-at-top logic to reduce duplication

This branch was successfully deployed

1 active deployment
Preview — 893c909b Deployed Apr 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot dependencies Pull requests that update a dependency file size/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant